import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

/**
 * TicTacToe.java Copyright (c) Sportnerds.
 * <P></P>
 * TicTacToe class takes handles all the business logic of the game.
 * @author Sportnerds
 * @version 1.0
 * @since 24.11.2011
 */
public class TicTacToe {
    protected Board board = new Board();
    protected Player[] players;
    protected boolean gameOver;
    protected int movesCount;
    protected boolean whosTurn;
    protected BufferedReader in;

    /**
     * Constructor to initialize a new TicTacToe game.
     * Initializes the input stream and an array of players.
     */
    public TicTacToe()
    {
        InputStreamReader reader = new InputStreamReader(System.in);
        in = new BufferedReader(reader);
        players = new Player[2];
    }

    /**
     * This function runs the game in the GameRunner class.
     * The only public function of the class besides the constructor.
     */
    public void Play()
    {
        System.out.println("\n Welcome to...\n");
        System.out.println("888888 88  dP77b8     888888    db     dP99b8     888888  dP7Yb  888888");
        System.out.println("  88   88 dP   79       88     dPYb   dP   79       88   dP   Yb 88__  ");
        System.out.println("  88   88 Yb            88    dP__Yb  Yb            88   Yb   dP 8899  ");
        System.out.println("  88   88  YboodP       88   dP7777Yb  YboodP       88    YbodP  888888\n");

        initializePlayers();
        boolean answer;     // Represents if another game should be played or not
        String input = "n";
        do
        {
            initializeGame();
            System.out.println("Lets start the game!\n");
            run();
            System.out.println("Play again? (y/n)");
            try
            {
                input = in.readLine();
            }
            catch (Exception ex)
            {
                // Do not repeat game on invalid input
                input = "n";
            }
            if(input.isEmpty())
                input = "n";
            answer = (input.charAt(0)=='y'||input.charAt(0)== 'Y');
        }
        while(answer);
    }

    /**
     * Void function to initialize a new round of the game
     */
    protected void initializeGame()
    {
        // Create empty board
        board = new Board();
        gameOver = false;
        movesCount = 0;
        whosTurn = true;
    }

    /**
     * Void function to give a new players a name.
     */
    protected void initializePlayers()
    {
        int playerIndex = 1;

        while (playerIndex >= 0)
        {
            String name = "";
            System.out.println("Who is player " + (whosTurn ? 'O' : 'X') + " (enter name):");
            try
            {
                name = in.readLine();
            }
            catch (IOException ex)
            {
                // Name should be empty on invalid input
                name = "";
            }
            if (!name.isEmpty())
            {
                players[playerIndex] = new Player(name);

                // If X has gotten a name, index to 0.
                // When 0 has gotten a index the while loop will exit
                playerIndex--;

                // Flip turn
                whosTurn = !whosTurn;
            }
            else
                System.out.println("Not a valid name, please try again");
        }
    }

    /**
     * This function run the game until checkIfWon returns true and sets gameOver to true.
     */
    protected void run()
    {
        while(!gameOver)
        {
            turn();
            boolean isWinner = checkIfWon();
            if(isWinner)
            {
                board.display();
                System.out.println("Congratulations "+ players[whosTurn? 1 : 0].getName() + ", you won!");
                gameOver = true;
            }
            else if(!isWinner && movesCount == 9)
            {
                System.out.println("It's a draw");
                gameOver = true;
            }
            else
                whosTurn = !whosTurn;
        }
    }

    /**
     * Turn function handles one turn and takes player input
     */
    protected void turn()
    {
        System.out.println(players[whosTurn ? 1 : 0].getName() + ", select an available space (1-9):");
        board.display();

        boolean isValid = false;    // user input valid check
        int move = 0;               // selected space
        while(!isValid)
        {
            try
            {
                move = Integer.parseInt(in.readLine());
            }
            catch (Exception ex)
            {
                // Space set to 0 on invalid input
                move = 0;
            }
            if (move < 1 || move > 9)
            {
                System.out.println("Please enter a number between 1-9");
            }
            else if (board.getSpace(move-1) == 'X' || board.getSpace(move-1) == 'O')
            {
                System.out.println("That space is occupied");
            }
            else
                isValid = true;
        }
        // At this point we have a valid space for input
        board.setSpace(move-1, (whosTurn ? 'X' : 'O'));
        movesCount++;
    }

    /**
     * Do we have a winner in the game? Checks all possible winning scenarios
     * and calls checkLine function for each to see if there is a winner.
     * @return bool value. True if we have a winner and false if we don't have.
     */
    protected boolean checkIfWon()
    {
        int[] line= new int[3];
        //Check rows
        for(int i=0; i<3; i++)
        {
            line[0]=(3*i);
            line[1]=1+(3*i);
            line[2]=2+(3*i);
            if(checkLine(line))
                return true;
        }
        //Check columns
        for(int i=0; i<3; i++)
        {
            line[0]=i;
            line[1]=3+i;
            line[2]=6+i;
            if(checkLine(line))
                return true;
        }
        //Check diagonal right
        line[0]=0;
        line[1]=4;
        line[2]=8;
        if(checkLine(line))
                return true;
        // Check diagonal left
        line[0]=2;
        line[1]=4;
        line[2]=6;
        if(checkLine(line))
                return true;

        // If we get to here checkLine has been false for all lines
        return false;
    }

    /**
     * Sum up ASCII value from a possible winning row from the game board
     * to find out the winner.
     * @param spaces the value in the array is summarized
     * @return bool value to the CheckIfWon function
     */
    protected boolean checkLine(int[] spaces)
    {
        // Get ASCII values for spaces
        int sum = 0;
        for (int i=0; i<3; i++)
        {
            sum += (int)board.getSpace(spaces[i]);
        }
        // Check if ASCII values sum is X+X+X or O+O+O
        return (sum == 237 || sum == 264);
    }
}
